package edu.ccut.saturn.component.loader;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.osgi.framework.Bundle;

import edu.ccut.saturn.component.IBaseComponentInfo;
import edu.ccut.saturn.component.IComponentInfo;
import edu.ccut.saturn.component.IConnectionInfo;
import edu.ccut.saturn.component.ILogger;
import edu.ccut.saturn.component.INodeInfo;
import edu.ccut.saturn.component.SaturnComponentException;
import edu.ccut.saturn.component.SaturnLoggerManager;
import edu.ccut.saturn.component.impl.SaturnActionComponentInfo;
import edu.ccut.saturn.component.impl.SaturnBaseComponentInfo;
import edu.ccut.saturn.component.impl.SaturnLogicComponentInfo;
import edu.ccut.saturn.component.impl.SaturnNodeConnectionInfo;
import edu.ccut.saturn.component.loader.node.NodeInfoLoader;
import edu.ccut.saturn.component.utils.LoaderUtils;
import edu.ccut.saturn.component.xml.Dom4jUtils;

/**
 * 加载action组件、logic组件、component组件的加载类。调用<code>NodeResolver</code>加载action组件和logic组件内部的结点。
 * 
 * @author 谷钰
 * 
 * @version 1.0 2008.9.03
 * 
 * @see NodeResolver
 * 
 */
public abstract class ComponentLoader {

	private static final String LOGGER_KEY_LOAD_BUNDLE = SaturnLoggerManager.LOGGER_KEY_LOAD_BUNDLE;

	protected static ILogger logger = SaturnLoggerManager.getInstance()
			.getSaturnLogger(LOGGER_KEY_LOAD_BUNDLE);

	public static final String KEY_COMPONET = "component";

	public static final String KEY_MODULE_KEY = "key";

	public static final String KEY_MODULE_NAME = "name";

	public static final String KEY_MODULE_DESCRIPTION = "description";
	
	public static final String KEY_COMPONENT_LOADSTART = "loadstart";

	public static final String KEY_COMPONENT_CLASS = "class";

	public static final String KEY_INFORMATION = "information";

	public static final String KEY_ACTION_ROOT = "saturn-action";

	public static final String KEY_LOGIC_ROOT = "saturn-logic";

	public static final String KEY_COMPONENT_ROOT = "saturn-components";

	public static final String KEY_CONNECTION = "connection";

	public static final String KEY_CONNECTION_TARGET = "target";

	public static final String KEY_CONNECTION_SOURCE = "source";

	public static final String KEY_CONNECTION_STYLE = "style";

	public static final String KEY_CONNECTION_NAME = "name";	

	/**
	 * 通过配置文件的文件名称读取component组件信息列表
	 * 
	 * @param bundle
	 * @param filePath
	 * @return
	 * @throws SaturnComponentException
	 */
	public static IComponentInfo loadBaseComponent(Bundle bundle,
			String filePath) throws SaturnComponentException {

		InputStream in = null;

		try {
			URL url = bundle.getEntry(filePath);
			logger.debug("BaseComponent-Start \"" + url+ "\"");
			in = url.openStream();
			return loadBaseComponent(bundle, in);

		} catch (IOException e) {
			logger.error("Found IOException! load file failed in bundle [key="
					+ bundle.getSymbolicName() + "] with file name ["
					+ filePath + "]", e);

			throw new SaturnComponentException(
					"load file failed in bundle [key="
							+ bundle.getSymbolicName() + "] with file name ["
							+ filePath + "]", e);

		} catch (Exception e) {
			throw new SaturnComponentException(
					"load file failed in bundle [key="
							+ bundle.getSymbolicName() + "] with file name ["
							+ filePath + "]", e);

		} finally {

			try {

				if (in != null) {
					in.close();
				}

			} catch (IOException e) {
				logger.error("file input stream close error!", e);
			}
			logger.debug("BaseComponent-End");
			logger.debug("");
		}
	}

	/**
	 * 通过InputStream读取component组件信息列表
	 * 
	 * @param bundleSymbolicName
	 *            组件所在Bundle的唯一标识
	 * @param in
	 * @return
	 * @throws DocumentException
	 * @throws SaturnComponentException
	 */
	@SuppressWarnings("unchecked")
	public static IComponentInfo loadBaseComponent(Bundle bundle, InputStream in)
			throws SaturnComponentException {

		Document document = Dom4jUtils.getDocment(in);
		Element root = document.getRootElement();
		Element component = root.element(KEY_COMPONET);
		IComponentInfo baseComponentInfo = null;

		try {
			baseComponentInfo = createBaseComponentInfo(bundle, component);

		} catch (ClassNotFoundException e) {
			logger.error("create base component failed.", e);
		}
		return baseComponentInfo;
	}

	/**
	 * 通过配置文件的文件名称读取logic组件信息
	 * 
	 * @param bundle
	 * @param filePath
	 * @return
	 * @throws SaturnComponentException
	 */
	public static IComponentInfo loadLogicComponentInfo(Bundle bundle,
			String filePath) throws SaturnComponentException {

		InputStream in = null;

		try {
			URL url = bundle.getEntry(filePath);
			logger.debug("LogicComponent-Start " + url + "\"");
			in = url.openStream();
			return loadLogicComponentInfo(bundle, in);

		} catch (IOException e) {
			logger.error("Found IOException! load file failed in bundle [key="
					+ bundle.getSymbolicName() + "] with file name ["
					+ filePath + "]", e);

			throw new SaturnComponentException(
					"load file failed in bundle [key="
							+ bundle.getSymbolicName() + "] with file name ["
							+ filePath + "]", e);

		} catch (Exception e) {
			throw new SaturnComponentException(
					"load file failed in bundle [key="
							+ bundle.getSymbolicName() + "] with file name ["
							+ filePath + "]", e);

		} finally {

			try {

				if (in != null) {
					in.close();
				}

			} catch (IOException e) {
				logger.error("file input stream close error!", e);
			}
			logger.debug("LogicComponent-End");
			logger.debug("");
		}
	}

	/**
	 * 读取logic组件信息。首先创建<code>SaturnLogicComponentInfo</code>的实例；
	 * 设置开始结点；解析组件内部的结点信息。
	 * 
	 * @param bundle
	 * @param in
	 * @return
	 * @throws SaturnComponentException
	 */
	public static IComponentInfo loadLogicComponentInfo(Bundle bundle,
			InputStream in) throws SaturnComponentException {

		Document document = Dom4jUtils.getDocment(in);
		Element root = document.getRootElement();
		SaturnLogicComponentInfo saturnLogicComponentInfo = createLogicComponentInfo(
				bundle, root);

		saturnLogicComponentInfo.setStartNodeInfo(NodeResolver.getStartNodeInfo(
				bundle, root));

		List<INodeInfo> nodes = NodeResolver.loadLogicComponentNodes(root,
				bundle);

		saturnLogicComponentInfo.addNodeInfos(nodes);
		return saturnLogicComponentInfo;
	}

	/**
	 * 读取展现组件信息
	 * 
	 * @param bundle
	 * @param filePath
	 * @return
	 * @throws SaturnComponentException
	 */
	public static IComponentInfo loadActionComponentInfo(Bundle bundle,
			String filePath) throws SaturnComponentException {

		InputStream in = null;

		try {
			URL url = bundle.getEntry(filePath);
			logger.debug("ActionComponent-Start : " + url + "\"");
			in = url.openStream();
			return loadActionComponentInfo(bundle, in);

		} catch (IOException exception) {
			logger.debug(
					"Error!Found IOException! load file failed in bundls [key="
							+ bundle.getSymbolicName() + "] with file name ["
							+ filePath + "]", exception);

			throw new SaturnComponentException(
					"load file failed in bundls [key="
							+ bundle.getSymbolicName() + "] with file name ["
							+ filePath + "]", exception);

		} catch (Exception e) {
			throw new SaturnComponentException("load file failed in bundls[key="
					+ bundle.getSymbolicName() + "] with file name ["
					+ filePath + "]", e);

		} finally {

			try {

				if (in != null) {
					in.close();
				}

			} catch (IOException e) {
				logger.error("file input stream close error!", e);
			}
			logger.debug("ActionComponent-End");
			logger.debug("");
		}
	}

	/**
	 * 加载展现组件配置文件信息。首先创建<code>SaturnActionComponentInfo</code>的实例；
	 * 设置开始结点；加载展现组件内部的结点信息。
	 * 
	 * @param bundle
	 * @param in
	 * @return
	 * @throws SaturnComponentException
	 */
	public static IComponentInfo loadActionComponentInfo(Bundle bundle,
			InputStream in) throws SaturnComponentException {

		Document document = Dom4jUtils.getDocment(in);
		Element root = document.getRootElement();
		SaturnActionComponentInfo saturnActionComponentInfo = createActionComponentInfo(
				bundle, root);

		saturnActionComponentInfo.setStartNodeInfo(NodeResolver
				.getStartNodeInfo(bundle, root));

		List<INodeInfo> nodes = NodeResolver.loadActionComponentNodes(root,
				bundle);

		saturnActionComponentInfo.addNodeInfos(nodes);
		return saturnActionComponentInfo;
	}

	@SuppressWarnings("unchecked")
	private static SaturnActionComponentInfo createActionComponentInfo(
			Bundle bundle, Element root) throws SaturnComponentException {

		Element information = root.element(KEY_INFORMATION);
		String key = information.elementTextTrim(KEY_MODULE_KEY);
		String name = information.elementTextTrim(KEY_MODULE_NAME);
		String description = information
				.elementTextTrim(KEY_MODULE_DESCRIPTION);

		logger.debug("  info : { symbolicName : '" + bundle.getSymbolicName() + "', '" + 
		                        "key : '" + key + "', '" + 
		                        "name : '" + name + "', '" + 
		                        "keydescription : '" + description + "'}" );
		SaturnActionComponentInfo saturnActionComponentInfo = new SaturnActionComponentInfo(
				bundle.getSymbolicName(), key, name, description);

		List<Element> connectionElements = root.elements(KEY_CONNECTION);
		List<IConnectionInfo> connectionInfos = loadConnectionInfos(connectionElements);
		saturnActionComponentInfo.setConnectionInfos(connectionInfos);

		return saturnActionComponentInfo;
	}

	@SuppressWarnings("unchecked")
	private static SaturnLogicComponentInfo createLogicComponentInfo(
			Bundle bundle, Element root) throws SaturnComponentException {

		Element information = root.element(KEY_INFORMATION);
		String key = information.elementTextTrim(KEY_MODULE_KEY);
		String name = information.elementTextTrim(KEY_MODULE_NAME);
		String description = information
				.elementTextTrim(KEY_MODULE_DESCRIPTION);
		String loadStart = information.elementTextTrim(KEY_COMPONENT_LOADSTART);

		logger.debug("  info : { symbolicName : '"+ bundle.getSymbolicName() + "', '" + 
				                "key : '" + key + "', '" +
				                "name : '" + name + "', '" +
				                "description : '" + description + "'}");
		
		List<Element> connectionElements = root.elements(KEY_CONNECTION);
		List<IConnectionInfo> connectionInfos = loadConnectionInfos(connectionElements);
		SaturnLogicComponentInfo saturnLogicComponentInfo = new SaturnLogicComponentInfo(
				bundle.getSymbolicName(), key, name, description, loadStart);

		saturnLogicComponentInfo.setConnectionInfos(connectionInfos);
		return saturnLogicComponentInfo;
	}

	private static IBaseComponentInfo createBaseComponentInfo(Bundle bundle,
			Element component) throws ClassNotFoundException {

		String componetClass = component.attributeValue(KEY_COMPONENT_CLASS);
		String key = component.attributeValue(KEY_MODULE_KEY).trim();
		String name = component.attributeValue(KEY_MODULE_NAME).trim();
		String description = component.elementTextTrim(KEY_MODULE_DESCRIPTION);
		String loadStart = component.elementTextTrim(KEY_COMPONENT_LOADSTART);
		Class<?> componentType = LoaderUtils.loadClass(bundle, componetClass);
		logger.debug("  info : { symbolicName : '" + bundle.getSymbolicName() + "', '" + 
				               " key = '" + key + "', '" + 
				               " name = '" + name + "', '" + 
				               " description = '" + description + "', '" + 
				               " componentType = '" + componentType + "'}" );

		return new SaturnBaseComponentInfo(bundle.getSymbolicName(), key, name,
				description, componentType, bundle.getSymbolicName(), NodeInfoLoader
						.getParameterInfos(bundle, component), loadStart);
	}

	private static List<IConnectionInfo> loadConnectionInfos(
			List<Element> connectionElements) {

		List<IConnectionInfo> connectionInfos = new ArrayList<IConnectionInfo>();

		if (connectionElements != null) {

			for (Element element : connectionElements) {
				IConnectionInfo connectionInfo = loadConnectionInfo(element);

				if (connectionInfo != null) {
					connectionInfos.add(connectionInfo);
				}
			}
		}
		return connectionInfos;
	}

	private static IConnectionInfo loadConnectionInfo(Element element) {

		if (element != null) {
			String target = element.attributeValue(KEY_CONNECTION_TARGET);
			String source = element.attributeValue(KEY_CONNECTION_SOURCE);
			String style = element.attributeValue(KEY_CONNECTION_STYLE);
			String name = element.attributeValue(KEY_CONNECTION_NAME);

			return new SaturnNodeConnectionInfo(target, source, style, name);
		}
		return null;
	}
}
